/*
 * OPIAM Suite
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package opiam.admin.faare.admin;

import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPSearchResults;
import netscape.ldap.LDAPv2;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeSet;


/**
 * Tools which generates faare_mapping.xml data from object classes read from directory.
 */
public final class LdapObjectLoader
{
    /** Utility class. */
    private LdapObjectLoader()
    {
    }

    /** Usage string. */
    private static final String USAGE = "Arguments are : host, port, class name, list of object classes";

    /**
     * Gets values from a string with tokens.
     * @param val Initial string
     * @param sep Separator
     * @return list of values.
     */
    private static List cut(String val, String sep)
    {
        List result = new ArrayList();
        StringTokenizer tok = new StringTokenizer(val, sep);

        while (tok.hasMoreTokens())
        {
            result.add(tok.nextToken().trim().toLowerCase());
        }

        return result;
    }

    /**
     * Gets a value from a string formatted as for example NAME 'name'.
     * @param val Initial string (NAME 'name')
     * @param key Keyword to cut (NAME)
     * @param sep First separator (')
     * @param sep2 Second separator (')
     * @return Result string (name) or null if keyword or separators are not present.
     */
    private static String get(String val, String key, String sep, String sep2)
    {
        String tok = key + " " + sep;
        int pos = val.indexOf(tok);

        if (pos == -1)
        {
            return null;
        }

        int pos2 = val.indexOf(sep2, pos + tok.length());

        if (pos2 == -1)
        {
            return null;
        }

        return val.substring(pos + tok.length(), pos2).trim().toLowerCase();
    }

    /**
     * Gets a value from a string formatted as for example NAME name.
     * @param val Initial string (NAME name)
     * @param key Keyword to cut (NAME)
     * @return Result string (name) or null if keyword is not present.
     */
    private static String get2(String val, String key)
    {
        String tok = key + " ";
        int pos = val.indexOf(tok);

        if (pos == -1)
        {
            return null;
        }

        int pos2 = val.indexOf(" ", pos + tok.length());

        if (pos2 == -1)
        {
            return null;
        }

        return val.substring(pos + tok.length(), pos2).trim().toLowerCase();
    }

    /**
     * Gets a syntax name from its OID.
     * @param syxoid Syntax OID.
     * @return Syntax name. Default is string.
     */
    public static String getSyntax(String syxoid)
    {
        if (syxoid.equals("1.3.6.1.4.1.1466.115.121.1.5"))
        {
            return "bytes";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.6"))
        {
            return "bytes";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.8"))
        {
            return "bytes";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.9"))
        {
            return "bytes";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.10"))
        {
            return "bytes";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.12"))
        {
            return "opiam.admin.faare.persistence.javabeans.JBTop";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.23"))
        {
            return "bytes";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.27"))
        {
            return "int";
        }

        if (syxoid.startsWith("1.3.6.1.4.1.1466.115.121.1.28"))
        {
            return "bytes";
        }

        return "string";
    }

    /**
     * Entry point of the tool.<br>
     *
     * @param args Command line arguments are :
     * - directory server host
     * - directory server port
     * - Java class name
     * - Comma seperated list of LDAP object classes
     */
    public static void main(String[] args)
    {
        if (args.length != 4)
        {
            System.out.println(USAGE);

            return;
        }

        try
        {
            String host = args[0];
            int port = Integer.parseInt(args[1]);

            LDAPConnection ld = new LDAPConnection();
            ld.connect(host, port);
            ld.setOption(LDAPv2.PROTOCOL_VERSION, new Integer("3"));

            String[] schema = {"subschemasubentry"};

            LDAPSearchResults res = ld.search("", LDAPv2.SCOPE_BASE,
                    "objectclass=*", schema, false);

            if (!res.hasMoreElements())
            {
                System.out.println("Can not get schema subentry name.");

                return;
            }

            LDAPEntry entry = res.next();
            String schemadn = entry.getAttribute("subschemasubentry")
                                   .getStringValueArray()[0];
            String[] ocls = {"objectClasses"};

            res = ld.search(schemadn, LDAPv2.SCOPE_BASE,
                    "objectclass=*", ocls, false);

            if (!res.hasMoreElements())
            {
                System.out.println("Can not get schema subentry.");

                return;
            }

            entry = res.next();

            String[] oclStrs = entry.getAttribute("objectClasses")
                                    .getStringValueArray();
            String requestedOclsStr = args[3];
            List requestedOcls = cut(requestedOclsStr, ",");

            TreeSet attribs = new TreeSet();

            for (int i = 0; i < oclStrs.length; i++)
            {
                String val = oclStrs[i];
                String name = get(val, "NAME", "'", "'");

                if (name == null)
                {
                    continue;
                }

                if (requestedOcls.contains(name))
                {
                    System.out.println("*** " + name + " found.");

                    String mustStr = get(val, "MUST", "(", ")");

                    if (mustStr != null)
                    {
                        List mustAtts = cut(mustStr, "$");
                        attribs.addAll(mustAtts);
                    }

                    String mayStr = get(val, "MAY", "(", ")");

                    if (mayStr != null)
                    {
                        List mayAtts = cut(mayStr, "$");
                        attribs.addAll(mayAtts);
                    }
                }
            }

            Iterator it = attribs.iterator();
            System.out.println("*** Attributs :");

            while (it.hasNext())
            {
                System.out.println(it.next());
            }

            String[] attyps = {"attributeTypes"};

            res = ld.search(schemadn, LDAPv2.SCOPE_BASE,
                    "objectclass=*", attyps, false);

            if (!res.hasMoreElements())
            {
                System.out.println("Can not get schema subentry.");

                return;
            }

            entry = res.next();

            String[] attsStrs = entry.getAttribute("attributeTypes")
                                     .getStringValueArray();

            ld.disconnect();

            List lattribs = new ArrayList();

            for (int i = 0; i < attsStrs.length; i++)
            {
                String val = attsStrs[i];
                String name = get(val, "NAME", "'", "'");

                if (name == null)
                {
                    // attributs qui ont plusieurs noms (ex: cn == commonName)
                    name = get(val, "NAME (", "'", "'");
                }

                if (attribs.contains(name))
                {
                    System.out.println("*** " + name + " found.");

                    String syntax = get2(val, "SYNTAX");

                    if (syntax == null)
                    {
                        syntax = "string";
                    }

                    System.out.println("*** " + name + " is " +
                        getSyntax(syntax));

                    boolean multi = true;

                    if ((val.indexOf("SINGLE-VALUE")) != -1)
                    {
                        multi = false;
                        System.out.println("*** " + name + " is mono valued");
                    }
                    else
                    {
                        System.out.println("*** " + name + " is multi valued");
                    }

                    Hashtable acur = new Hashtable();
                    acur.put("nom", name);

                    String objtype = getSyntax(syntax);
                    acur.put("type", objtype);

                    if (multi)
                    {
                        acur.put("collection", "collection");

                        if (objtype.equals("bytes"))
                        {
                            acur.put("lazy",
                                "opiam.admin.faare.persistence.LazyBytesCollection");
                        }
                        else if (objtype.equals(
                                    "opiam.admin.faare.persistence.javabeans.JBTop"))
                        {
                            acur.put("lazy",
                                "opiam.admin.faare.persistence.LazyJBTopCollection");
                        }
                    }

                    lattribs.add(acur);
                }
            }

            List objet = new ArrayList();
            Hashtable tobj = new Hashtable();
            tobj.put("class", args[2]);
            tobj.put("mapto", args[3]);
            tobj.put("attribs", lattribs);
            objet.add(tobj);

            XMLWriter.genereFaareMappingObj(objet);
        }
        catch (Exception ex)
        {
            System.out.println("*** " + ex.getMessage());
            ex.printStackTrace();
        }
    }
}
